#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import re
from typing import Optional, Union, List, Tuple, Dict, Any
import markdown
from bs4 import BeautifulSoup, Tag, Comment
from bs4.element import NavigableString
from docx import Document
from docx.shared import Pt, Inches, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
from docx.enum.style import WD_STYLE_TYPE
from docx.oxml.ns import qn
from pygments import highlight
from pygments.lexers import get_lexer_by_name, TextLexer
from pygments.token import Token, STANDARD_TYPES
from pygments.formatter import Formatter


class DocxFormatter(Formatter):
    """将Pygments标记转换为可以应用到python-docx运行的格式"""

    def __init__(self, **options):
        super().__init__(**options)
        # 为不同的标记类型定义样式
        self.styles = {
            Token.Keyword: {
                "color": RGBColor(0x00, 0x77, 0xFF),
                "bold": True,
            },  # 蓝色加粗
            Token.String: {"color": RGBColor(0xCC, 0x33, 0x00)},  # 红色
            Token.Name.Function: {
                "color": RGBColor(0x00, 0x88, 0x00),
                "bold": True,
            },  # 绿色加粗
            Token.Comment: {
                "color": RGBColor(0x99, 0x99, 0x99),
                "italic": True,
            },  # 灰色斜体
            Token.Number: {"color": RGBColor(0xFF, 0x66, 0x00)},  # 橙色
            Token.Operator: {"color": RGBColor(0x66, 0x66, 0x66)},  # 深灰色
            Token.String.Doc: {
                "color": RGBColor(0x99, 0x99, 0x99),
                "italic": True,
            },  # 灰色斜体
            Token.Name.Decorator: {"color": RGBColor(0x99, 0x33, 0x99)},  # 紫色
            Token.Name.Class: {
                "color": RGBColor(0x00, 0x77, 0x77),
                "bold": True,
            },  # 青色加粗
            Token.Name.Builtin: {"color": RGBColor(0x99, 0x33, 0x00)},  # 深红色
            Token.Name.Constant: {"color": RGBColor(0x88, 0x00, 0x88)},  # 紫色
            Token.Name.Namespace: {"color": RGBColor(0x00, 0x88, 0x88)},  # 青色
            Token.Name.Exception: {
                "color": RGBColor(0xCC, 0x00, 0x00),
                "bold": True,
            },  # 红色加粗
            Token.Name.Variable: {"color": RGBColor(0x00, 0x66, 0x99)},  # 深蓝色
            Token.Literal: {"color": RGBColor(0xCC, 0x66, 0x00)},  # 橙色
            Token.Punctuation: {"color": RGBColor(0x66, 0x66, 0x66)},  # 深灰色
            Token.Text: {"color": RGBColor(0x00, 0x00, 0x00)},  # 黑色
        }
        self.default_style = {"color": RGBColor(0x00, 0x00, 0x00)}  # 默认黑色

    def _get_style(self, tokentype):
        """获取给定标记类型的样式"""
        # 首先尝试完全匹配
        if tokentype in self.styles:
            return self.styles[tokentype]

        # 然后尝试父类型匹配
        current = tokentype
        while current is not Token:
            if current in self.styles:
                return self.styles[current]
            current = current.parent

        # 如果是空白字符，返回默认样式
        if tokentype in [Token.Text, Token.Text.Whitespace]:
            return self.default_style

        # 对于其他未知类型，返回默认样式
        return self.default_style

    def format(self, tokensource, outfile):
        """格式化标记流"""
        result = []
        for ttype, value in tokensource:
            style = self._get_style(ttype)
            result.append((style, value))
        return result


class MarkdownToDocx:
    def __init__(self):
        self.document = Document()
        self.current_list_level = 0
        self.list_style = None
        self.base_path = ""

        # 设置默认字体
        self._set_default_font()

        # 创建样式
        self._create_styles()

    def _set_default_font(self):
        """设置文档默认字体"""
        self.document.styles["Normal"].font.name = "Times New Roman"
        self.document.styles["Normal"]._element.rPr.rFonts.set(qn("w:eastAsia"), "宋体")
        self.document.styles["Normal"].font.size = Pt(12)

    def _create_styles(self):
        """创建文档样式"""
        # 标题样式
        for i in range(1, 7):
            style_name = f"Heading {i}"
            if style_name not in self.document.styles:
                style = self.document.styles.add_style(
                    style_name, WD_STYLE_TYPE.PARAGRAPH
                )
            else:
                style = self.document.styles[style_name]
            style.font.name = "Times New Roman"
            style._element.rPr.rFonts.set(qn("w:eastAsia"), "黑体")
            style.font.size = Pt(20 - i * 2)  # h1=18pt, h2=16pt, etc.
            style.font.bold = True

        # 代码块样式
        code_style = self.document.styles.add_style(
            "Code Block", WD_STYLE_TYPE.PARAGRAPH
        )
        code_style.font.name = "Consolas"  # 更改为Consolas字体
        code_style.font.size = Pt(10)
        code_style.paragraph_format.space_before = Pt(6)
        code_style.paragraph_format.space_after = Pt(6)
        code_style.paragraph_format.line_spacing = 1.0
        code_style.paragraph_format.first_line_indent = Inches(0)  # 确保没有首行缩进
        code_style.paragraph_format.left_indent = Inches(0.3)  # 整体左侧缩进
        code_style.paragraph_format.right_indent = Inches(0.3)  # 整体右侧缩进

    def convert(self, markdown_file: str, output_file: str):
        """将Markdown文件转换为Word文档"""
        self.base_path = os.path.dirname(os.path.abspath(markdown_file))

        # 读取并转换Markdown
        with open(markdown_file, "r", encoding="utf-8") as f:
            markdown_text = f.read()

        # 转换Markdown为HTML
        html = markdown.markdown(
            markdown_text,
            extensions=[
                "tables",
                "fenced_code",
                "codehilite",
                "nl2br",
                "attr_list",
                "md_in_html",
            ],
            extension_configs={
                "codehilite": {
                    "css_class": "highlight",
                    "use_pygments": True,
                    "noclasses": True,
                    "guess_lang": False,
                }
            },
        )

        # 调试输出
        print("Generated HTML:")
        print(html)

        # 解析HTML
        soup = BeautifulSoup(html, "html.parser")

        # 移除所有HTML注释
        for comment in soup.find_all(string=lambda text: isinstance(text, Comment)):
            comment.extract()

        # 处理HTML元素
        self._process_elements(soup)

        # 保存文档
        self.document.save(output_file)

    def _process_elements(self, soup):
        """处理HTML元素"""
        for element in soup.children:
            if isinstance(element, NavigableString):
                if str(element).strip():
                    p = self.document.add_paragraph()
                    p.add_run(str(element))
            elif isinstance(element, Tag):
                self._process_tag(element)

    def _process_tag(self, tag: Tag):
        """处理HTML标签"""
        if tag.name in ["h1", "h2", "h3", "h4", "h5", "h6"]:
            self._process_heading(tag)
        elif tag.name == "p":
            self._process_paragraph(tag)
        elif tag.name == "ul":
            self._process_list(tag, numbered=False)
        elif tag.name == "ol":
            self._process_list(tag, numbered=True)
        elif tag.name == "table":
            self._process_table(tag)
        elif tag.name == "pre":
            self._process_code_block(tag)
        elif tag.name == "blockquote":
            self._process_quote(tag)
        else:
            # 处理其他标签的子元素
            for child in tag.children:
                if isinstance(child, Tag):
                    self._process_tag(child)
                elif isinstance(child, NavigableString) and str(child).strip():
                    p = self.document.add_paragraph()
                    p.add_run(str(child))

    def _process_heading(self, tag: Tag):
        """处理标题"""
        level = int(tag.name[1])
        p = self.document.add_paragraph(style=f"Heading {level}")
        self._process_inline_elements(tag, p)

    def _process_paragraph(self, tag: Tag):
        """处理段落"""
        p = self.document.add_paragraph()
        self._process_inline_elements(tag, p)

    def _process_inline_elements(self, tag: Tag, paragraph):
        """处理行内元素"""
        for element in tag.children:
            if isinstance(element, NavigableString):
                run = paragraph.add_run(str(element))
            elif isinstance(element, Tag):
                if element.name == "strong":
                    run = paragraph.add_run(element.get_text())
                    run.bold = True
                elif element.name == "em":
                    run = paragraph.add_run(element.get_text())
                    run.italic = True
                elif element.name == "code":
                    run = paragraph.add_run(element.get_text())
                    run.font.name = "Consolas"
                    run.font.size = Pt(10)
                elif element.name == "img":
                    self._process_image(element, paragraph)
                elif element.name == "a":
                    self._process_link(element, paragraph)

    def _process_list(self, tag: Tag, numbered: bool):
        """处理列表"""
        self.current_list_level += 1
        for item in tag.find_all("li", recursive=False):
            p = self.document.add_paragraph(
                style="List Bullet" if not numbered else "List Number"
            )
            p.paragraph_format.left_indent = Inches(self.current_list_level * 0.25)
            self._process_inline_elements(item, p)

            # 处理嵌套列表
            nested_lists = item.find_all(["ul", "ol"], recursive=False)
            for nested_list in nested_lists:
                self._process_list(nested_list, numbered="ol" in nested_list.name)

        self.current_list_level -= 1

    def _process_table(self, tag: Tag):
        """处理表格"""
        rows = tag.find_all("tr")
        if not rows:
            return

        # 获取列数
        cols = len(rows[0].find_all(["th", "td"]))
        table = self.document.add_table(rows=len(rows), cols=cols)
        table.style = "Table Grid"

        for i, row in enumerate(rows):
            cells = row.find_all(["th", "td"])
            for j, cell in enumerate(cells):
                table_cell = table.cell(i, j)
                self._process_inline_elements(cell, table_cell.paragraphs[0])

        self.document.add_paragraph()  # 添加空行

    def _process_code_block(self, tag: Tag):
        """处理代码块"""
        # 获取代码和语言
        code_tag = tag.find("code")
        if code_tag:
            code = code_tag.get_text()
        else:
            code = tag.get_text()

        # 尝试从代码内容推断语言
        code_lines = code.strip().split("\n")
        first_line = code_lines[0].lower() if code_lines else ""
        code_content = code.lower()
        language = ""

        # 基于代码特征推断语言
        if any(marker in first_line for marker in ["def ", "class ", "import ", "#"]):
            language = "python"
        elif any(
            marker in first_line
            for marker in ["function", "const ", "let ", "var ", "//"]
        ):
            language = "javascript"
        # 对于Java，检查整个代码内容
        elif (
            "public class" in code_content
            or "private class" in code_content
            or "protected class" in code_content
            or "import java" in code_content
            or "package " in code_content
            or "/**" in first_line
            or "public static void main" in code_content
        ):
            language = "java"
        elif any(
            marker in first_line
            for marker in ["select ", "create table", "insert into", "--"]
        ):
            language = "sql"
        elif any(
            marker in first_line for marker in ["<!doctype", "<html", "<head", "<body"]
        ):
            language = "html"

        # 调试信息
        print(f"Inferred language: '{language}' from content")

        # 添加语言标识（如果有）
        if language:
            lang_p = self.document.add_paragraph(style="Code Block")
            lang_run = lang_p.add_run(f"Language: {language}")
            lang_run.font.color.rgb = RGBColor(0x66, 0x66, 0x66)
            lang_run.italic = True

        try:
            # 获取词法分析器
            if language:
                print(f"Using lexer for language: {language}")
                lexer = get_lexer_by_name(language.lower())
            else:
                print("Using text lexer")
                lexer = TextLexer()
        except Exception as e:
            print(f"Error getting lexer: {e}")
            lexer = TextLexer()

        # 去除代码开头和结尾的空白字符，但保留缩进
        code = code.strip("\n")

        # 直接使用格式化器
        formatter = DocxFormatter()

        try:
            tokens = list(lexer.get_tokens(code))
            print(f"Language: {language}")
            print(f"First few tokens: {tokens[:3]}")  # 只打印前几个token用于调试
            highlighted = formatter.format(tokens, "")
        except Exception as e:
            print(f"Error during tokenization: {e}")
            # 如果分词失败，回退到纯文本
            highlighted = [({"color": RGBColor(0, 0, 0)}, code)]

        # 创建代码块段落
        p = self.document.add_paragraph(style="Code Block")

        # 应用高亮
        if highlighted:
            for style, value in highlighted:
                run = p.add_run(value)
                run.font.name = "Consolas"
                run.font.size = Pt(10)

                # 应用样式
                if "color" in style:
                    run.font.color.rgb = style["color"]
                if style.get("bold", False):
                    run.bold = True
                if style.get("italic", False):
                    run.italic = True
        else:
            # 如果没有高亮结果，直接添加原始代码
            run = p.add_run(code)
            run.font.name = "Consolas"
            run.font.size = Pt(10)

        # 添加空行
        self.document.add_paragraph()

    def _process_quote(self, tag: Tag):
        """处理引用"""
        p = self.document.add_paragraph()
        p.paragraph_format.left_indent = Inches(0.5)
        p.paragraph_format.right_indent = Inches(0.5)
        p.paragraph_format.space_before = Pt(6)
        p.paragraph_format.space_after = Pt(6)
        p.style = "Quote"
        self._process_inline_elements(tag, p)

    def _process_image(self, tag: Tag, paragraph):
        """处理图片"""
        src = tag.get("src", "")
        if src:
            # 处理相对路径
            if not os.path.isabs(src):
                src = os.path.join(self.base_path, src)

            if os.path.exists(src):
                try:
                    paragraph.add_run().add_picture(src, width=Inches(6))
                except Exception as e:
                    print(f"Warning: Failed to add image {src}: {e}")

    def _process_link(self, tag: Tag, paragraph):
        """处理链接"""
        url = tag.get("href", "")
        text = tag.get_text()
        run = paragraph.add_run(text)
        run.font.color.rgb = RGBColor(0, 0, 255)
        run.font.underline = True


def main(md_file, docx_file):
    # import argparse
    #
    # parser = argparse.ArgumentParser(description="将Markdown转换为Word文档")
    # parser.add_argument("input", help="输入的Markdown文件")
    # parser.add_argument("output", help="输出的Word文档")
    # args = parser.parse_args()

    converter = MarkdownToDocx()
    converter.convert(md_file, docx_file)
    print(f"finished converting {md_file} to {docx_file}")


if __name__ == "__main__":
    path = os.path.dirname(os.path.abspath(__file__))
    # title = "firework"
    name = "006"
    # md_file = os.path.join(path, f"{name}/{name}.md")
    # docx_file = os.path.join(path, f"{name}/{name}.docx")
    md_file = os.path.join(path, "doc", name, "main.md")
    docx_file = os.path.join(path, "doc", name, "main.docx")

    main(md_file, docx_file)
